package org.litu.base.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.litu.base.entity.StaticFieldEnum;
import org.litu.base.log.LtLogOperation;
import org.litu.base.log.LtLogOperationEnum;
import org.litu.base.service.IBaseService;
import org.litu.core.base.ApiRes;
import org.litu.core.base.BaseController;
import org.litu.core.base.BaseEntity;
import org.litu.core.base.SelectVo;
import org.litu.core.enums.ResultEnum;
import org.litu.core.exception.LtParamException;
import org.litu.core.login.TokenCheck;
import org.litu.util.common.FieldUtil;
import org.litu.util.common.ValidateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import java.util.*;

/**
 * 通用单表操作controller
 *
 * @author yueye
 */
@TokenCheck
public abstract class BaseFormController<T extends BaseEntity, S extends IBaseService<T>> extends BaseController {
    @Autowired
    protected S service;

    /**
     * 列表，不分页
     *
     * @param entity 实体类
     * @return 列表
     */
    @GetMapping("/list")
    @ResponseBody
    @ApiOperation(value = "列表查询", notes = "查询列表信息，所有数据", httpMethod = "GET")
    @ApiImplicitParam(name = "keyword", value = "关键字，模糊检索，字段默认未使用", dataType = "String")
    public ApiRes<List<T>> list(T entity, @RequestParam(defaultValue = "") String keyword) {
        Map<String, String> params = requestParams();

        List<T> list = service.list(nowUser(), entity, keyword, params);
        return ApiRes.ok(list);
    }

    /**
     * 请求处理方法会调用此方法， 子类可根据情况重写该方法
     *
     * @param entity 集合
     * @return 树结构集合
     */
    protected SelectVo toSelect(T entity) {
        // 记录显示值
        String label = "";
        if (FieldUtil.hasProperty(entity, StaticFieldEnum.Name.getCode())) {
            label = Objects.requireNonNull(FieldUtil.read(entity, StaticFieldEnum.Name.getCode())).toString();
        }
        return new SelectVo(entity.getId(), label);
    }

    /**
     * 下拉列表
     *
     * @param entity 实体类
     * @return 下拉列表
     */
    @GetMapping("/select")
    @ResponseBody
    @ApiOperation(value = "获取下拉选择框列表", notes = "获取下拉选择框列表", httpMethod = "GET")
    public ApiRes<List<SelectVo>> select(T entity) {
        Map<String, String> params = requestParams();
        List<T> list = service.list(nowUser(), entity, "", params);

        // 将列表转化成树结构
        List<SelectVo> selectVos = new ArrayList<>();
        for (T node : list) {
            selectVos.add(toSelect(node));
        }
        return ApiRes.ok(selectVos);
    }

    /**
     * 分页查询的排序处理
     *
     * @param page      分页实体
     * @param sortName  排序的字段名称,数据库的字段名称
     * @param sortOrder 排序的方式
     */
    protected void pageSort(Page<T> page, String sortName, String sortOrder) {
        // 正序标识
        String ascStr = "asc";

        if (ascStr.equals(sortOrder)) {
            page.setAsc(sortName);
        } else {
            page.setDesc(sortName);
        }
    }

    /**
     * 列表，分页
     *
     * @param entity 实体类
     * @return 列表，分页
     */
    @GetMapping("/page")
    @ResponseBody
    @ApiOperation(value = "获取分页列表", notes = "获取分页列表数据", httpMethod = "GET")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "curPage", value = "第几页，第一页为0", required = true, defaultValue = "0", dataType = "String"),
            @ApiImplicitParam(name = "pageSize", value = "每页条数", required = true, defaultValue = "10", dataType = "String"),
            @ApiImplicitParam(name = "sortName", value = "排序列,目前仅支持一个列，传入多列无效", dataType = "String"),
            @ApiImplicitParam(name = "sortOrder", value = "排序方式: asc-升序,默认，desc-降序", dataType = "String"),
            @ApiImplicitParam(name = "keyword", value = "关键字，模糊检索，字段默认未使用", dataType = "String")
    })
    public ApiRes<IPage<T>> page(T entity, @RequestParam(name = "curPage", defaultValue = "0") Integer curPage,
                                 @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, String sortName,
                                 String sortOrder, @RequestParam(name = "keyword", defaultValue = "") String keyword) {
        Page<T> page = new Page<>(curPage, pageSize);

        // 添加分页查询的排序项
        if (StringUtils.isNotBlank(sortName)) {
            pageSort(page, sortName, sortOrder);
        }

        Map<String, String> params = requestParams();
        IPage<T> pages = service.page(nowUser(), entity, keyword, page, params);
        return ApiRes.ok(pages);
    }

    /**
     * 获取实体详细信息
     *
     * @param id 实体id
     * @return 实体信息
     */
    @GetMapping("/detail")
    @ResponseBody
    @ApiOperation(value = "查看详情", notes = "根据ID获取实体的详情信息", httpMethod = "GET")
    @ApiImplicitParam(name = "id", value = "要获取的实体对象的id", required = true, dataType = "String")
    public ApiRes detail(@RequestParam(required = true) String id) {
        if (StringUtils.isBlank(id)) {
            return ApiRes.error(ResultEnum.ParamError);
        }

        T obj = service.detail(id);
        return ApiRes.ok(obj);
    }

    /**
     * 添加、修改时，进行传入数据字段的校验
     *
     * @param entity 对象实体
     * @param params 其他参数
     */
    protected void validate(T entity, Map<String, String> params) {
        // 使用javax中的验证，对字段进行验证
        Set<ConstraintViolation<T>> constraintViolations = ValidateUtil.validate(entity);
        if (constraintViolations != null && constraintViolations.size() != 0) {
            throw new LtParamException(constraintViolations.iterator().next().getMessage());
        }
    }

    /**
     * 保存
     *
     * @param entity 实体类
     * @return 保存成功或者是保存错误枚举
     */
    @PostMapping("/add")
    @ResponseBody
    @LtLogOperation(operation = LtLogOperationEnum.ADD)
    @ApiOperation(value = "新增", notes = "将传入的数据，新增到数据库", httpMethod = "POST")
    public ApiRes add(T entity) {
        Map<String, String> params = requestParams();

        validate(entity, params);

        boolean res = service.save(nowUser(), entity, params);
        return res ? ApiRes.ok("保存成功！") : ApiRes.error(ResultEnum.SaveError);
    }

    /**
     * 更新
     *
     * @param entity 实体类
     * @return 更新成功或更新错误枚举
     */
    @PostMapping("/update")
    @ResponseBody
    @LtLogOperation(operation = LtLogOperationEnum.UPDATE)
    @ApiOperation(value = "编辑修改", notes = "根据传入的更新的实体信息，进行数据更新，默认根据id进行更新", httpMethod = "POST")
    public ApiRes update(T entity) {
        Map<String, String> params = requestParams();
        validate(entity, params);

        boolean res = service.update(nowUser(), entity, params);
        return res ? ApiRes.ok("更新成功！") : ApiRes.error(ResultEnum.UpdateError);
    }

    /**
     * 物理删除
     *
     * @param id 传入要删除的记录的id
     * @return 删除成功或删除错误枚举
     */
    @PostMapping("/delete")
    @ResponseBody
    @LtLogOperation(operation = LtLogOperationEnum.DELETE)
    @ApiOperation(value = "物理删除", notes = "根据ID物理删除相关数据", httpMethod = "POST")
    @ApiImplicitParam(name = "id", value = "要获取的实体对象的主键", required = true, dataType = "String")
    public ApiRes delete(@RequestParam(required = true) String id) {
        if (StringUtils.isBlank(id)) {
            return ApiRes.error(ResultEnum.ParamError, "id不能为空");
        }

        Map<String, String> params = requestParams();

        boolean res = service.delete(id, params);
        return res ? ApiRes.ok("删除成功！") : ApiRes.error(ResultEnum.DeleteError);
    }

    /**
     * 批量物理删除
     *
     * @param ids 要批量删除的记录id集合字符串  用，隔开
     * @return 删除成功或者删除错误枚举
     */
    @PostMapping("/batchDelete")
    @ResponseBody
    @LtLogOperation(operation = LtLogOperationEnum.BATCHDELETE)
    @ApiOperation(value = "批量物理删除", notes = "根据传入的要删除数据的id，全部删除掉", httpMethod = "POST")
    @ApiImplicitParam(name = "ids", value = "要批量删除的记录id集合字符串,，隔开", required = true, dataType = "String")
    public ApiRes batchDelete(@RequestParam(required = true) String ids) {
        if (StringUtils.isBlank(ids)) {
            return ApiRes.error(ResultEnum.ParamError, "删除信息不能为空。");
        }

        Map<String, String> params = requestParams();

        // 将id字符串转为数组
        String[] idArray = ids.split(",");
        List<String> idList = new ArrayList<>();
        Collections.addAll(idList, idArray);

        boolean res = service.batchDelete(idList, params);
        return res ? ApiRes.ok("删除成功！") : ApiRes.error(ResultEnum.DeleteError);
    }
}
